//
//  FLVDemuxer.cpp
//  VideoParse
//
//  Created by xiaerfei on 2020/4/1.
//  Copyright © 2020 erfeixia. All rights reserved.
//

#include "FLVDemuxer.hpp"
#include "AVIOContext.hpp"
#include "AVFormatContext.hpp"
#include <iostream>
#include <string.h>
#include "intfloat.hpp"
#include "mathematics.hpp"

#define TYPE_ONTEXTDATA 1
#define TYPE_ONCAPTION 2
#define TYPE_ONCAPTIONINFO 3
#define TYPE_UNKNOWN 9


/* AMF date type */
typedef struct amf_date {
    double   milliseconds;
    int16_t  timezone;
} amf_date;


/*
 Signature(3 Byte)+Version(1 Byte)+Flags(1 Bypte)+DataOffset(4 Byte)

 signature 占3个字节
    固定FLV三个字符作为标示。一般发现前三个字符为FLV时就认为他是flv文件。
 Version 占1个字节
    标示FLV的版本号。这里我们看到是1
 Flags 占1个字节
    内容标示。第0位和第2位,分别表示 video 与 audio 存在的情况.(1表示存在,0表示不存在)。截图看到是0x05，也就是00000101，代表既有视频，也有音频。
 DataOffset 4个字节
    表示FLV的header长度。这里可以看到固定是9
 
 */
int FLVDemuxer::read_header(void *s) {
    AVIOContext *pb = ((AVFormatContext *)s)->pb;
    // skip 4
//    pb->avio_rb32();
    pb->avio_skip(4);
    int flags = pb->avio_r8();
    this->missing_streams = flags & (FLV_HEADER_FLAG_HASVIDEO | FLV_HEADER_FLAG_HASAUDIO);
    
    int offset = pb->avio_rb32();
    
    int pre_tag_size = pb->avio_rb32();
    if (pre_tag_size) {
        std::cout << "Read FLV header error, input file is not a standard flv format, first PreviousTagSize0 always is 0\n" << std::endl;
    }
    
    return 0;
}

int FLVDemuxer::read_packet(void *s, void *pkt) {
    AVIOContext *pb = ((AVFormatContext *)s)->pb;
    /*
     tag header 11 byte
     Type        : 1 byte tag 类型: 音频(0x08)、视频(0x09)、script data(0x12)
     DataSize    : 3 byte 该 tag Ddata 的大小
     Timestamp   : 3 byte 该 tag 的时间戳
     Timestamp_ex: 1 byte 时间戳扩展字节
     StreamID    : 3 byte stream id
     */
    int orig_size = 0, size = 0, flags = 0;
    int64_t dts, pts = 0;
    enum FlvTagType type;
    int stream_type =-1;
    int64_t next, pos, meta_pos;
    int last = -1;
    
    pos = pb->avio_tell();
    // Type
    type = (FlvTagType)(pb->avio_r8() & 0x1F);
    // DataSize
    orig_size = size = pb->avio_rb24();
    
    this->sum_flv_tag_size += size + 11;
    // Timestamp
    dts  = pb->avio_rb24();
    dts |= (unsigned)pb->avio_r8() << 24;

    if (pb->eof_reached) {
        return -1;
    }
    // StreamID
    pb->avio_rb24();/* stream id, always 0 */
    
    if (size == 0) {
        return -1;
    }
    
    next = size + pb->avio_tell();
    
    if (type == FLV_TAG_TYPE_AUDIO) {
        stream_type = FLV_STREAM_TYPE_AUDIO;
        flags    = pb->avio_r8();
        size--;
    } else if (type == FLV_TAG_TYPE_VIDEO) {
        stream_type = FLV_STREAM_TYPE_VIDEO;
        flags    = pb->avio_r8();
        size--;
        if ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_VIDEO_INFO_CMD)
            goto skip;
    } else if (type == FLV_TAG_TYPE_META) {
        stream_type = FLV_STREAM_TYPE_SUBTITLE;
        /*
            AMF
            类型(1 byte) | 长度( 2 byte) | stringData(根据长度)
               0x02         0x000A          onMetaData
         
            第一个 AMF 包 == 13 byte
         
            第二个 AMF 包
            类型(1 byte) | 长度( 4 byte) 数组长度 | 数组开始
         
            故有下面👇 size > 13 + 1 + 4
         
         */
        if (size > 13 + 1 + 4) { // Header-type metadata stuff
            int type;
            meta_pos = pb->avio_tell();
            type = flv_read_metabody(s, next);
            if ((type == 0 && dts == 0) || type < 0) {
                
            }
            goto skip;
        }
    } else {
skip:
        if (pb->avio_seek(next, SEEK_SET) != next) {
            return -1;
        }
        goto leave;
    }
    
    if (!size) {
        goto leave;
    }
    
    
    
leave:
    last = pb->avio_rb32();
    if (!trust_datasize) {
        
    }
    
    
    return 0;
}

int FLVDemuxer::read_seek(void *s, int stream_index, int64_t ts, int flags) {
    
    return 0;
}

int FLVDemuxer::read_close(void *s) {
    
    return 0;
}


#pragma mark - private methods

int FLVDemuxer::flv_read_metabody(void *s, int64_t next) {
    AVIOContext *pb = ((AVFormatContext *)s)->pb;
    AMFDataType type;
    AVStream *stream, *astream = nullptr, *vstream = nullptr;
    int i = 0;
    char buffer[32];
    // first object needs to be "onMetaData" string
    type = (AMFDataType)pb->avio_r8();
    if (type != AMF_DATA_TYPE_STRING || amf_get_string(s, buffer, sizeof(buffer)) < 0) {
        return TYPE_UNKNOWN;
    }
    
    if (!strcmp(buffer, "onTextData"))
        return TYPE_ONTEXTDATA;

    if (!strcmp(buffer, "onCaption"))
        return TYPE_ONCAPTION;

    if (!strcmp(buffer, "onCaptionInfo"))
        return TYPE_ONCAPTIONINFO;

    if (strcmp(buffer, "onMetaData") && strcmp(buffer, "onCuePoint")) {
        std::cout << "Unknown type " << buffer << std::endl;
        return TYPE_UNKNOWN;
    }
    
    
    // find the streams now so that amf_parse_object doesn't need to do
    // the lookup every time it is called.
    for (i = 0; i < ((AVFormatContext *)s)->nb_streams; i++) {
        stream = ((AVFormatContext *)s)->streams[i];
        if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            vstream = stream;
            this->last_keyframe_stream_index = i;
        } else if (stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
            astream = stream;
            if (this->last_keyframe_stream_index == -1)
                this->last_keyframe_stream_index = i;
        }
        else if (stream->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) {
            // unuse
        }
    }
    
    if (amf_parse_object(s, astream, vstream, buffer, next, 0) < 0) {
        return -1;
    }
    return 0;
}

int FLVDemuxer::amf_get_string(void *s, char *buffer, int buffsize) {
    AVIOContext *pb = ((AVFormatContext *)s)->pb;
    int length = pb->avio_rb16();
    if (length > buffsize) {
        pb->avio_skip(length);
        return -1;
    }
    pb->read_packet((u_int8_t *)buffer, length);
    buffer[length] = '\0';
    return length;
}

int FLVDemuxer::amf_parse_object(void *s, void *astream, void *vstream, const char *key, int64_t max_pos, int depth) {
    AVIOContext *pb = ((AVFormatContext *)s)->pb;
    AMFDataType amf_type;
    char str_val[1024];
    double num_val = 0;
    amf_date date;
    
    AVCodecParameters *apar, *vpar;
    
    amf_type = (AMFDataType)pb->avio_r8();
    
    switch (amf_type) {
        case AMF_DATA_TYPE_NUMBER:
            num_val = IntFloat::av_int2double(pb->avio_rb64());
            break;
        case AMF_DATA_TYPE_BOOL:
            num_val = pb->avio_r8();
            break;
        case AMF_DATA_TYPE_STRING:
            if (amf_get_string(s, str_val, sizeof(str_val)) < 0) {
                std::cout << "AMF_DATA_TYPE_STRING parsing failed" << std::endl;
                return -1;
            }
            break;
        case AMF_DATA_TYPE_OBJECT: {
            if (key && depth == 1) {
                if (parse_keyframes_index(s, max_pos) < 0) {
                    return -1;
                } else {
                    add_keyframes_index(s);
                }
            }
            
            unsigned v;
            while (pb->avio_tell() < max_pos - 2 && amf_get_string(s, str_val, sizeof(str_val)) > 0) {
                if (amf_parse_object(s, astream, vstream, str_val, max_pos, depth + 1) < 0) {
                    return -1;
                }
                
            }
            v = pb->avio_r8();
            if (v != AMF_END_OF_OBJECT) {
                return -1;
            }
            break;
        }
        case AMF_DATA_TYPE_NULL:
        case AMF_DATA_TYPE_UNDEFINED:
        case AMF_DATA_TYPE_UNSUPPORTED:
            break;     // these take up no additional space
        case AMF_DATA_TYPE_MIXEDARRAY: {
            unsigned v;
            pb->avio_skip(4);
            /*             572 573 574 575
                -   -   -   -   -   -   -
                *   *   *   0   0   9   max_pos
                                👆  == max_pos - 2
             
                当读完所有的属性后 pos = 572, 此时  pos < max_pos - 2( 573 )
                在读一次 amf_get_string 2byte  pos = 572 + 2 = 574 > 573
                
                v = pb->avio_r8();( pos == 574)
             */
            while (pb->avio_tell() < max_pos - 2 && amf_get_string(s, str_val, sizeof(str_val)) > 0) {
                std::cout << str_val << std::endl;
                if (amf_parse_object(s, astream, vstream, str_val, max_pos, depth + 1) < 0) {
                    return -1;
                }
                
            }
            v = pb->avio_r8();
            if (v != AMF_END_OF_OBJECT) {
                return -1;
            }
            break;
        }
        case AMF_DATA_TYPE_ARRAY: {
            unsigned int arraylen, i;
            arraylen = pb->avio_rb32();
            for (i = 0; i < arraylen && pb->avio_tell() < max_pos - 1; i++) {
                if (amf_parse_object(s, NULL, NULL, NULL, max_pos,depth + 1) < 0) {
                    return -1;
                }
            }
            break;
        }
        case AMF_DATA_TYPE_DATE: {
            date.milliseconds = IntFloat::av_double2int(pb->avio_rb64());
            date.timezone = pb->avio_rb16();
            break;
        }
    
        default: {
            std::cout << "unsupported amf type: " << amf_type << std::endl;
            return -1;
        }
    }
    if (key) {
        apar = astream ? ((AVStream *)astream)->codecpar : NULL;
        vpar = vstream ? ((AVStream *)astream)->codecpar : NULL;

        if (depth == 1) {
            /*
             audiocodecid       Number    音频编解码器 ID
             audiodatarate      Number    音频码率，单位 kbps
             audiodelay         Number    由音频编解码器引入的延时，单位秒
             audiosamplerate    Number    音频采样率
             audiosamplesize    Number    音频采样点尺寸
             canSeekToEnd       Boolean   指示最后一个视频帧是否是关键帧
             creationdate       String    创建日期与时间
             duration           Number    文件总时长，单位秒
             filesize           Number    文件总长度，单位字节
             framerate          Number    视频帧率
             height             Number    视频高度，单位像素
             stereo             Boolean   音频立体声标志
             videocodecid       Number    视频编解码器 ID
             videodatarate      Number    视频码率，单位 kbps
             width              Number    视频宽度，单位像素
             */
            if (amf_type == AMF_DATA_TYPE_NUMBER || amf_type == AMF_DATA_TYPE_BOOL) {
                if (!strcmp(key, "duration")) {
                    // 视频的时长，这里扩大了一百万倍至微秒，避免产生小数
                    ((AVFormatContext *)s)->duration = num_val * AV_TIME_BASE;
                } else if (!strcmp(key, "videodatarate") && 0 <= (int)(num_val * 1024.0)) {
                    // kbps * 1024 = bps
                    this->video_bit_rate = num_val * 1024.0;
                } if (!strcmp(key, "audiodatarate") && 0 <= (int)(num_val * 1024.0)) {
                    // kbps * 1024 = bps
                    this->audio_bit_rate = num_val * 1024.0;
                } else if (!strcmp(key, "datastream")) {
                    
                } else if (!strcmp(key, "framerate")) {
                    this->framerate = rational::av_d2q(num_val, 1000);
                    if (vstream) {
                        ((AVStream *)vstream)->avg_frame_rate = this->framerate;
                    }
                } else if (this->trust_metadata && vpar) {
                    if (!strcmp(key, "videocodecid")) {
                        int ret = flv_set_video_codec(s, vstream, num_val, 0);
                        if (ret < 0) {
                            return ret;
                        }
                    } else if (!strcmp(key, "audiocodecid") && apar) {
                        flv_set_audio_codec(s, astream, apar, num_val);
                    } else if (!strcmp(key, "audiosamplerate") && apar) {
                        apar->sample_rate = num_val;
                    } else if (!strcmp(key, "audiosamplesize") && apar) {
                        apar->bits_per_coded_sample = num_val;
                    } else if (!strcmp(key, "stereo") && apar) {
                        apar->channels = num_val + 1;
//                        apar->channel_layout = apar->channels == 2 ?
//                        AV_CH_LAYOUT_STEREO :
//                        AV_CH_LAYOUT_MONO;
                    } else if (!strcmp(key, "width") && vpar) {
                        vpar->width = num_val;
                    } else if (!strcmp(key, "height") && vpar) {
                        vpar->height = num_val;
                    }
                }
            }
            
            if (amf_type == AMF_DATA_TYPE_STRING) {
                if (!strcmp(key, "encoder")) {
                    //FIXME:encoder暂时不处理
                } else if (!strcmp(key, "metadatacreator")) {
                    if (!strcmp (str_val, "MEGA") || !strncmp(str_val, "FlixEngine", 10)) {
                        //FIXME:metadatacreator暂时不处理
                    }
                }
            }
        }
        
        if (amf_type == AMF_DATA_TYPE_OBJECT) {
            //FIXME:AMF_DATA_TYPE_OBJECT 暂时不处理
        }
        
        if ((!strcmp(key, "duration")        ||
            !strcmp(key, "filesize")        ||
            !strcmp(key, "width")           ||
            !strcmp(key, "height")          ||
            !strcmp(key, "videodatarate")   ||
            !strcmp(key, "framerate")       ||
            !strcmp(key, "videocodecid")    ||
            !strcmp(key, "audiodatarate")   ||
            !strcmp(key, "audiosamplerate") ||
            !strcmp(key, "audiosamplesize") ||
            !strcmp(key, "stereo")          ||
            !strcmp(key, "audiocodecid")    ||
            !strcmp(key, "datastream"))) {
            
            return 0;
        }
        if (amf_type == AMF_DATA_TYPE_BOOL) {
            std::string str = str_val;
            ((AVFormatContext *)s)->add_value_for_key(num_val > 0?"true":"false", str);
            
        } else if (amf_type == AMF_DATA_TYPE_NUMBER) {
            
        } else if (amf_type == AMF_DATA_TYPE_STRING) {
            
        } else if (amf_type == AMF_DATA_TYPE_DATE) {
            time_t time;
            struct tm t;
            char datestr[128];
            time =  date.milliseconds / 1000; // to seconds
            localtime_r(&time, &t);
            strftime(datestr, sizeof(datestr), "%a, %d %b %Y %H:%M:%S %z", &t);
            ((AVFormatContext *)s)->add_value_for_key(datestr,key);
        }
        
    }
    return 0;
}

int FLVDemuxer::parse_keyframes_index(void *s, int64_t max_pos) {
    
    
    return 0;
}

void FLVDemuxer::add_keyframes_index(void *s) {
    
}

int FLVDemuxer::flv_set_video_codec(void *s, void *vstream, int flv_codecid, int read) {
    AVStream *vs = (AVStream *)vstream;
    AVFormatContext *vc = (AVFormatContext *)s;
    int ret = 0;
    
    AVCodecParameters *par = vs->codecpar;
    enum AVCodecID old_codec_id = vs->codecpar->codec_id;
    
    switch (flv_codecid) {
        case FLV_CODECID_H263:par->codec_id = AV_CODEC_ID_FLV1;break;
        case FLV_CODECID_REALH263:par->codec_id = AV_CODEC_ID_H263;break;
        case FLV_CODECID_SCREEN:par->codec_id = AV_CODEC_ID_FLASHSV;break;
        case FLV_CODECID_SCREEN2:par->codec_id = AV_CODEC_ID_FLASHSV2;break;
        case FLV_CODECID_VP6:par->codec_id = AV_CODEC_ID_VP6F;break;
        case FLV_CODECID_VP6A:
        {
            if (flv_codecid == FLV_CODECID_VP6A) {
                par->codec_id = AV_CODEC_ID_H263;
            }
            
            break;
        }
        case FLV_CODECID_H264: {
            par->codec_id = AV_CODEC_ID_H264;
            //FIXME: 暂时不知道这里是干啥的？
            vs->need_parsing = AVSTREAM_PARSE_HEADERS;
            ret = 3;     // not 4, reading packet type will consume one byte
        }
        case FLV_CODECID_MPEG4: {
            par->codec_id = AV_CODEC_ID_MPEG4;
            ret = 3;
            break;
        }
        default: {
            par->codec_tag = flv_codecid;
            break;
        }
    }
    
    
    return ret;
}

void FLVDemuxer::flv_set_audio_codec(void *s, void *astream, void *avCodecParameters, int flv_codecid) {
    AVStream *as = (AVStream *)astream;
    AVFormatContext *vc = (AVFormatContext *)s;
    AVCodecParameters *apar = (AVCodecParameters *)avCodecParameters;
    
    switch (flv_codecid) {
        case FLV_CODECID_PCM:
        {
            apar->codec_id = apar->bits_per_coded_sample == 8 ? AV_CODEC_ID_PCM_U8:AV_CODEC_ID_PCM_S16LE;
        }
            break;
        case FLV_CODECID_PCM_LE: {
            apar->codec_id = apar->bits_per_coded_sample == 8
                               ? AV_CODEC_ID_PCM_U8
                               : AV_CODEC_ID_PCM_S16LE;
            break;
        }
        case FLV_CODECID_AAC: {
            apar->codec_id = AV_CODEC_ID_AAC;
            break;
        }
        case FLV_CODECID_ADPCM: {
            apar->codec_id = AV_CODEC_ID_ADPCM_SWF;
            break;
        }
        case FLV_CODECID_SPEEX:
            apar->codec_id    = AV_CODEC_ID_SPEEX;
            apar->sample_rate = 16000;
            break;
        case FLV_CODECID_MP3:
            apar->codec_id      = AV_CODEC_ID_MP3;
            as->need_parsing = AVSTREAM_PARSE_FULL;
            break;
        case FLV_CODECID_NELLYMOSER_8KHZ_MONO:
            // in case metadata does not otherwise declare samplerate
            apar->sample_rate = 8000;
            apar->codec_id    = AV_CODEC_ID_NELLYMOSER;
            break;
        case FLV_CODECID_NELLYMOSER_16KHZ_MONO:
            apar->sample_rate = 16000;
            apar->codec_id    = AV_CODEC_ID_NELLYMOSER;
            break;
        case FLV_CODECID_NELLYMOSER:
            apar->codec_id = AV_CODEC_ID_NELLYMOSER;
            break;
        case FLV_CODECID_PCM_MULAW:
            apar->sample_rate = 8000;
            apar->codec_id    = AV_CODEC_ID_PCM_MULAW;
            break;
        case FLV_CODECID_PCM_ALAW:
            apar->sample_rate = 8000;
            apar->codec_id    = AV_CODEC_ID_PCM_ALAW;
            break;
        default:
            apar->codec_tag = flv_codecid >> FLV_AUDIO_CODECID_OFFSET;
            break;
    }
}
